#pragma once
#include <vector>
namespace tge
{
#ifndef geom_tol
#define geom_tol 1.0e-7
#endif
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
	template<class RealT>
	class TGeVector2
	{
	public:
		typedef RealT value_type;
		RealT x, y;
		static const TGeVector2 kIdentity;
		static const TGeVector2 kXAxis;
		static const TGeVector2 kYAxis;
	public:
		TGeVector2()
		{
			x = 0;
			y = 0;
		}
		TGeVector2(RealT xx, RealT yy) :x(xx), y(yy)
		{}
		TGeVector2& set(double xx, double yy)
		{
			x = xx;
			y = yy;
			return *this;
		}
		bool operator !=(const TGeVector2 &v) const
		{
			const value_type tol = 1.0e-7;
			if (fabs(x - v.x) > tol)return true;
			if (fabs(y - v.y) > tol)return true;
			return false;
		}
		bool operator ==(const TGeVector2 &vec) const
		{
			const value_type eps = 1.0e-6;
			if (fabs(x - vec.x) > eps)return false;
			if (fabs(y - vec.y) > eps)return false;
			return true;
		}
		TGeVector2 operator+(const TGeVector2&v)const
		{
			return TGeVector2(x + v.x, y + v.y);
		}
		TGeVector2& operator+=(const TGeVector2&v)
		{
			x += v.x;
			y += v.y;
			return *this;
		}
		TGeVector2 operator-(const TGeVector2&v)const
		{
			return TGeVector2(-x, -y);
		}
		TGeVector2& operator-=(const TGeVector2&v)
		{
			x -= v.x;
			y -= v.y;
			return *this;
		}
		TGeVector2 operator - ()const
		{
			return TGeVector2(-x, -y);
		}
		friend TGeVector2 operator *(double scale, const TGeVector2&v)
		{
			return TGeVector2(scale*v.x, scale*v.y);
		}
		friend TGeVector2 operator *(const TGeVector2&v, double scale)
		{
			return TGeVector2(scale*v.x, scale*v.y);
		}
		TGeVector2 operator *(double scale)
		{
			return TGeVector2(x*scale, y*scale);
		}
		TGeVector2& operator *=(double scale)
		{
			x *= scale;
			y *= scale;
			return *this;
		}
		TGeVector2& operator /=(double scale)
		{
			x /= scale;
			y /= scale;
			return *this;
		}
		TGeVector2 operator /(double scale)const
		{
			return TGeVector2(x / scale, y / scale);
		}
		RealT operator [](int i)const
		{
			return *(&x + i);
		}
		RealT& operator [](int i)
		{
			return *(&x + i);
		}
		TGeVector2& rotateBy(double angle)
		{
			double sinv = sin(angle);
			double cosv = cos(angle);
			double xx = x*cosv - y*sinv;
			double yy = x*sinv + y*cosv;
			x = xx;
			y = yy;
			return *this;
		}
		RealT length() const
		{
			return sqrt(x*x + y*y);
		}

		RealT lengthSqrd() const
		{
			return x*x + y*y;
		}
		TGeVector2& normalize(RealT tol=geom_tol)
		{
			RealT dd = lengthSqrd();
			if (dd < tol*tol)return *this;
			RealT len = sqrt(dd);
			x /= len;
			y /= len;
			return *this;
		}
		double angle() const
		{
			TGeVector2 v(*this);
			v.normalize();
			if (y < 0)
			{
				return M_PI * 2 - acos(v.dotProduct(TGeVector2::kXAxis));
			}
			double t = v.dotProduct(TGeVector2::kXAxis);
			if (t > 1.0)t = 1.0;
			if (t < -1.0)t = -1.0;
			return acos(t);
		}
		double operator^(const TGeVector2 &vec) const
		{
			return x*vec.y - y*vec.x;
		}
		double dotProduct(const TGeVector2 &v) const
		{
			return x*v.x + y*v.y;
		}
		TGeVector2 normal(double tol=geom_tol) const
		{
			if (isUnitLength(tol) || isZeroLength(tol))return *this;
			double len = length();
			return TGeVector2(x / len, y / len);
		}
		bool isZeroLength(double tol=geom_tol) const
		{
			return lengthSqrd() < tol*tol;
		}
		BOOL isUnitLength(double tol=geom_tol) const
		{
			double dd = lengthSqrd();
			return dd<(1 + 2 * tol + tol*tol) && dd>(1 - 2 * tol + tol*tol);
		}
	};
	template<class RealT>
	const TGeVector2<RealT> TGeVector2<RealT>::kIdentity(0, 0);
	template<class RealT>
	const TGeVector2<RealT> TGeVector2<RealT>::kXAxis(1, 0);
	template<class RealT>
	const TGeVector2<RealT> TGeVector2<RealT>::kYAxis(0, 1);
	//////////////////////////////////////////////////////////////////////
	template<class RealT>
	class TGePoint2
	{
		typedef TGeVector2<RealT> VECTOR2;
	public:
		typedef RealT value_type;
		RealT x, y;
	public:
		TGePoint2()
		{
			x = 0;
			y = 0;
		}
		TGePoint2(RealT xx, RealT yy) :x(xx), y(yy)
		{
		}
		TGePoint2& set(RealT xx, RealT yy)
		{
			x = xx;
			y = yy;
			return *this;
		}
	public:
		TGePoint2 operator*(RealT scale)const
		{
			return TGePoint2(x*scale, y*scale);
		}
		friend TGePoint2 operator *(RealT scale, const TGePoint2&pt)
		{
			return TGePoint2(pt.x*scale, pt.y*scale);
		}
		TGePoint2&operator*=(RealT scale)
		{
			x *= scale;
			y *= scale;
			return *this;
		}
		TGePoint2 operator/(RealT scale)const
		{
			return TGePoint2(x / scale, y / scale);
		}
		TGePoint2&operator/=(RealT scale)
		{
			x /= scale;
			y /= scale;
			return *this;
		}
		TGePoint2    operator +  (const VECTOR2& vec) const
		{
			return TGePoint2(x + vec.x, y + vec.y);
		}
		TGePoint2&   operator += (const VECTOR2& vec)
		{
			x += vec.x;
			y += vec.y;
			return *this;
		}
		TGePoint2    operator -  (const VECTOR2& vec) const
		{
			return TGePoint2(x - vec.x, y - vec.y);
		}
		TGePoint2&   operator -= (const VECTOR2& vec)
		{
			x -= vec.x;
			y -= vec.y;
			return *this;
		}
		VECTOR2   operator -  (const TGePoint2& pnt) const
		{
			return VECTOR2(x - pnt.x, y - pnt.y);
		}
		bool operator == (const TGePoint2& pnt) const
		{
			if (fabs(x - pnt.x) > TOL)return false;
			if (fabs(y - pnt.y) > TOL)return false;
			return true;
		}
		bool operator != (const TGePoint2& pnt) const
		{
			if (fabs(x - pnt.x) > TOL)return true;
			if (fabs(y - pnt.y) > TOL)return true;
			return false;
		}
		RealT operator [] (unsigned int idx) const
		{
			return *(&x + idx);
		}
		RealT& operator [] (unsigned int idx)
		{
			return *(&x + idx);
		}
		friend TGePoint2 operator+(const TGePoint2&p1, const TGePoint2&p2)
		{
			return TGePoint2(p1.x + p2.x, p1.y + p2.y);
		}
		bool isEqualTo(const TGePoint2& pnt, RealT tol = 1.0e-6)const
		{
#ifndef SQ
#define SQ(x) ((x)*(x))
#endif
			return SQ(pnt.x - x) + SQ(pnt.y - y) < tol*tol;
		}
		VECTOR2   asVector() const
		{
			return VECTOR2(x, y);
		}
		RealT    distanceTo(const TGePoint2& pnt) const
		{
#ifndef SQ
#define SQ(x) ((x)*(x))
#endif
			return sqrt(SQ(x - pnt.x) + SQ(y - pnt.y));
		}
	};

	typedef TGeVector2<float> GeVector2f;
	typedef TGeVector2<double> GeVector2d;
	typedef TGePoint2<float> GePoint2f;
	typedef TGePoint2<double> GePoint2d;
	typedef std::vector<GePoint2d> GePoint2dArray;
	typedef std::vector<GeVector2f> GeVector2fArray;
}